//
// Created by twetec on 25-8-17.
//

#include "../include/network_prober/probe_detect/handle_mongodb_probe_detect.h"

#include <boost/json.hpp>
#include <bson/bson.h>
#include <mongoc/mongoc.h>

#include <network_prober/utils/constants.h>

using namespace std::literals::chrono_literals;

namespace network_prober::probe_detect
{
void tag_invoke(boost::json::value_from_tag, boost::json::value &jv, MongoDBProbeDetectResultIPv4 const &c)
{
    auto& ja = jv.emplace_object();

    ja["interface"] = c.interface;
    ja["target"] = c.target;
    ja["port"] = c.port;
    ja["delay"] = c.delay;
}

HandleMongoDBProbeDetectIPv4::HandleMongoDBProbeDetectIPv4(
    std::string_view interface,
    std::string_view target,
    std::uint32_t port,
    std::string_view userName,
    std::string_view password,
    std::string_view databaseName,
    std::string_view queryStatement
)
    : m_interface(interface), m_target(target), m_port(port), m_userName(userName),
    m_password(password), m_databaseName(databaseName), m_queryStatement(queryStatement)
{
    // 初始化libmongoc库，在应用程序启动时，只调用一次
    std::call_once(utils::g_mongoClientInitOnceFlag, [] {
        // 初始化正常
        mongoc_init();
    });
}

void HandleMongoDBProbeDetectIPv4::detect()
{
    // 建立连接时，开始计时
    m_detectStartTime = std::chrono::steady_clock::now();

    // 指向当前对象的shared_ptr
    auto self{shared_from_this()};

    // 发起探测
    m_onDetectedCallbackFuture = std::async(std::launch::async, [this, self] {
        // 探测结果
        MongoDBProbeDetectResultIPv4 result;
        result.interface = m_interface;
        result.target = m_target;
        result.port = m_port;

        // 加载mongoc驱动
        // mongoc_init();

        // 2、创建MongoDB客户端
        std::stringstream ss;
        ss << "mongodb://" << m_target << ":" << m_port;

        pthread_mutex_lock(&utils::g_mongoClientInitMutex);
        mongoc_client_t *client = mongoc_client_new(ss.str().c_str());
        pthread_mutex_unlock(&utils::g_mongoClientInitMutex);

        // 如果连接失败
        if (!client) {
            // 报错信息
            SPDLOG_INFO("MongoDB的client对象创建失败..");
            // 清除mongoc驱动
            // mongoc_cleanup();
            // 连接失败，延迟为-1
            result.delay = -1;
            // 回调结果
            m_onDetectedCallback(std::move(result));
            // 结束执行
            return;
        }

        // 设定超时时间
        mongoc_client_set_sockettimeoutms(client, 5 * 1000);

        // 准备ping命令
        bson_t *ping = BCON_NEW ("ping", BCON_INT32 (1));
        bson_t reply = BSON_INITIALIZER;
        bson_error_t error;

        // 发送ping命令，如果失败
        if (!mongoc_client_command_with_opts(client, "admin", ping, NULL, NULL, &reply, &error)) {
            // 报错信息
            SPDLOG_INFO("MongoDB的ping命令执行失败..");
            // 回收资源
            bson_destroy(&reply);
            bson_destroy(ping);
            mongoc_client_destroy (client);
            // 清除mongoc驱动
            // mongoc_cleanup();
            // 连接失败，延迟为-1
            result.delay = -1;
            // 回调结果
            m_onDetectedCallback(std::move(result));
            // 结束执行
            return;
        }

        // 回收资源
        bson_destroy (&reply);
        bson_destroy (ping);
        mongoc_client_destroy (client);
        // 清除mongoc驱动
        // mongoc_cleanup();

        // SQL正常执行完毕,计算延迟
        result.delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - m_detectStartTime).count();
        // 回调结果
        m_onDetectedCallback(std::move(result));
    });
}

void HandleMongoDBProbeDetectIPv4::setOnDetectedCallback(std::function<void(MongoDBProbeDetectResultIPv4&&)> callback)
{
    m_onDetectedCallback = std::move(callback);
}

HandleMongoDBProbeDetectIPv4::~HandleMongoDBProbeDetectIPv4()
{

}

void tag_invoke(boost::json::value_from_tag, boost::json::value &jv, MongoDBProbeDetectResultIPv6 const &c)
{
    auto& ja = jv.emplace_object();

    ja["interface"] = c.interface;
    ja["target"] = c.target;
    ja["port"] = c.port;
    ja["delay"] = c.delay;
}

HandleMongoDBProbeDetectIPv6::HandleMongoDBProbeDetectIPv6(
    std::string_view interface,
    std::string_view target,
    std::uint32_t port,
    std::string_view userName,
    std::string_view password,
    std::string_view databaseName,
    std::string_view queryStatement
)
    : m_interface(interface), m_target(target), m_port(port), m_userName(userName),
    m_password(password), m_databaseName(databaseName), m_queryStatement(queryStatement)
{
    // 初始化libmongoc库，在应用程序启动时，只调用一次
    std::call_once(utils::g_mongoClientInitOnceFlag, [] {
        // 初始化正常
        mongoc_init();
    });
}

void HandleMongoDBProbeDetectIPv6::detect()
{
    // 建立连接时，开始计时
    m_detectStartTime = std::chrono::steady_clock::now();

    // 指向当前对象的shared_ptr
    auto self{shared_from_this()};

    // 发起探测
    m_onDetectedCallbackFuture = std::async(std::launch::async, [this, self] {
        // 探测结果
        MongoDBProbeDetectResultIPv6 result;
        result.interface = m_interface;
        result.target = m_target;
        result.port = m_port;

        // 加载mongoc驱动
        // mongoc_init();

        // 2、创建MongoDB客户端
        std::stringstream ss;
        ss << "mongodb://[" << m_target << "]:" << m_port;

        pthread_mutex_lock(&utils::g_mongoClientInitMutex);
        mongoc_client_t *client = mongoc_client_new(ss.str().c_str());
        pthread_mutex_unlock(&utils::g_mongoClientInitMutex);

        // 如果连接失败
        if (!client) {
            // 报错信息
            SPDLOG_INFO("MongoDB的client对象创建失败..");
            // 清除mongoc驱动
            // mongoc_cleanup();
            // 连接失败，延迟为-1
            result.delay = -1;
            // 回调结果
            m_onDetectedCallback(std::move(result));
            // 结束执行
            return;
        }

        // 设定超时时间
        mongoc_client_set_sockettimeoutms(client, 5 * 1000);

        // 准备ping命令
        bson_t *ping = BCON_NEW ("ping", BCON_INT32 (1));
        bson_t reply = BSON_INITIALIZER;
        bson_error_t error;

        // 发送ping命令，如果失败
        if (!mongoc_client_command_with_opts(client, "admin", ping, NULL, NULL, &reply, &error)) {
            // 报错信息
            SPDLOG_INFO("MongoDB的ping命令执行失败: {}", error.message);
            // 回收资源
            bson_destroy(&reply);
            bson_destroy(ping);
            mongoc_client_destroy(client);
            // 清除mongoc驱动
            // mongoc_cleanup();
            // 连接失败，延迟为-1
            result.delay = -1;
            // 回调结果
            m_onDetectedCallback(std::move(result));
            // 结束执行
            return;
        }

        // 回收资源
        bson_destroy (&reply);
        bson_destroy (ping);
        mongoc_client_destroy (client);
        // 清除mongoc驱动
        // mongoc_cleanup();

        // SQL正常执行完毕,计算延迟
        result.delay = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - m_detectStartTime).count();
        // 回调结果
        m_onDetectedCallback(std::move(result));
    });
}

void HandleMongoDBProbeDetectIPv6::setOnDetectedCallback(std::function<void(MongoDBProbeDetectResultIPv6&&)> callback)
{
    m_onDetectedCallback = std::move(callback);
}

HandleMongoDBProbeDetectIPv6::~HandleMongoDBProbeDetectIPv6()
{

}

}